package cpa.prowon.gui;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Properties;

import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;

import pho.prowon.category.IndexCategoryDefinition;
import pho.prowon.category.IndexCategoryDefinitionFactory;
import cpa.prowon.Application;
import cpa.prowon.gui.actions.ExitAction;
import cpa.prowon.gui.actions.HelpAboutAction;
import cpa.prowon.gui.actions.PreferencesAction;
import cpa.prowon.gui.util.ImageUtil;
import cpa.prowon.image.ImageContainer;

/**
 * This is the main window for the application. This window is opened by the
 * application and will be the main window, holding the category viewer, picture
 * list viewer and picture details
 * @author Chris
 */
public class MainWindow extends ApplicationWindow
{

	private static MainWindow _instance;
	private MenuManager fileMenu;
	private MenuManager editMenu;
	private MenuManager viewMenu;
	private WindowCategoryPart categoryPart;
	private WindowListPart listPart;
	private WindowPreviewPart previewPart;
	private Collection registeredWindowParts;
	
	/**
	 * Property name constant for whether or not to show the thumbnail preview
	 * @return The property name for whether or not to show the thumbnail preview
	 * @author Chris
	 */
	public static final String PROPNAME_SHOWTHUMBNAIL = "prowon.Thumbnails.Show";

	/**
	 * Property name constant for the thumbnail size
	 * @return The property name for the thumbnail size
	 * @author Chris
	 */
	public static final String PROPNAME_THUMBNAILSIZE = "prowon.Thumbnails.Size";

	/**
	 * Property name constant for the x value of the window
	 * @return The property name for the x value of the window
	 * @author Chris
	 */
	protected static final String PROPNAME_X = "prowon.MainWindow.x";

	/**
	 * Property name constant for the y value of the window
	 * @return The property name for the y value of the window
	 * @author Chris
	 */
	protected static final String PROPNAME_Y = "prowon.MainWindow.y";

	/**
	 * Property name constant for the width of the window
	 * @return The property name for the width of the window
	 * @author Chris
	 */
	protected static final String PROPNAME_WIDTH = "prowon.MainWindow.width";

	/**
	 * Property name constant for the height of the window
	 * @return The property name for the height of the window
	 * @author Chris
	 */
	protected static final String PROPNAME_HEIGHT = "prowon.MainWindow.height";

	/**
	 * Property name constant for whether or not the window is maximized
	 * @return The property name for whether ot not the window is maximized
	 * @author Chris
	 */
	protected static final String PROPNAME_MAXIMIZED = "prowon.MainWindow.maximized";

	/**
	 * Property name constant for whether or not the window is minimized
	 * @return The property name for whether ot not the window is minimized
	 * @author Chris
	 */
	protected static final String PROPNAME_MINIMIZED = "prowon.MainWindow.minimized";

	/**
	 * Get the sole instance of the main window, if it does not exist,
	 * then create a new one
	 * @return The main window for the application
	 * @author Chris
	 */
	public static MainWindow getInstance()
	{
		if (_instance == null)
		{
			_instance = new MainWindow();
		};
		return _instance;
		
	}
	
	/**
	 * Constructor
	 * @author Chris
	 */
	private MainWindow()
	{
		super(null);
		createWindowParts();
		addStatusLine();
		addMenuBar();
		addToolBar(SWT.FLAT | SWT.WRAP);
	}
	
	/**
	 * Create the window parts
	 * @author Chris
	 */
	protected void createWindowParts()
	{

		registeredWindowParts = new ArrayList();
		
		categoryPart = new WindowCategoryPart(this);
		registeredWindowParts.add(categoryPart);

		listPart = new WindowListPart(this);
		registeredWindowParts.add(listPart);
		
		previewPart = new WindowPreviewPart(this);
		registeredWindowParts.add(previewPart);

	}

	/**
	 * Create the window menu
	 * @author Chris
	 */
	protected MenuManager createMenuManager()
	{
		
		// Create the main menu bar
		MenuManager bar_menu = new MenuManager("");
		
		// Create the file menu
		setFileMenu(new MenuManager(Application.getInstance().getResourceBundle().getString("menu_item_file")));
		bar_menu.add(getFileMenu());
		Iterator fileIterator = registeredWindowParts.iterator();
		while (fileIterator.hasNext())
		{
			WindowPart aPart = (WindowPart) fileIterator.next();
			aPart.registerFileMenuActions(getFileMenu());
		}
		getFileMenu().add(new Separator());
		getFileMenu().add(new PreferencesAction(this));
		getFileMenu().add(new Separator());
		getFileMenu().add(new ExitAction(this));

		// Create the edit menu
		setEditMenu(new MenuManager(Application.getInstance().getResourceBundle().getString("menu_item_edit")));
		bar_menu.add(getEditMenu());
		Iterator editIterator = registeredWindowParts.iterator();
		while (editIterator.hasNext())
		{
			WindowPart aPart = (WindowPart) editIterator.next();
			aPart.registerEditMenuActions(getEditMenu());
		}

		// Create the view menu		
		setViewMenu(new MenuManager(Application.getInstance().getResourceBundle().getString("menu_item_view")));
		bar_menu.add(getViewMenu());
		Iterator viewIterator = registeredWindowParts.iterator();
		while (viewIterator.hasNext())
		{
			WindowPart aPart = (WindowPart) viewIterator.next();
			aPart.registerViewMenuActions(getViewMenu());
		}
		
		// Create the help menu
		MenuManager help_menu = new MenuManager(Application.getInstance().getResourceBundle().getString("menu_item_help"));
		bar_menu.add(help_menu);
		help_menu.add(new HelpAboutAction(this));

		return bar_menu;
		
	}
	
	/**
	 * Create the contents of the window
	 * @param Composite parent
	 * @return Control The control which holds all the contents 
	 * @author Chris
	 */
	protected Control createContents(Composite parent)
	{

		SashForm mainForm = new SashForm(parent, SWT.HORIZONTAL);
		categoryPart.createWidgets(mainForm);

		SashForm listForm = new SashForm(mainForm, SWT.VERTICAL);
		listPart.createWidgets(listForm);

		// Set the input for the categorization viewer
		Properties appProps = Application.getInstance().getApplicationProperties();
		IndexCategoryDefinition icd = null;
		String icdPropName = appProps.getProperty(Application.PROPNAME_INDEXCATEGORYDEFINITION_NAMEPROPERTY, "");
		String icdName = appProps.getProperty(Application.PROPNAME_INDEXCATEGORYDEFINITION_NAME, "");
		if (!icdPropName.equals("")) {
			icd = IndexCategoryDefinitionFactory.getInstance().definitionForProperty(icdPropName);
		} else if (!icdName.equals("")) {
			icd = IndexCategoryDefinitionFactory.getInstance().definitionFor(icdName);
		}
		if (icd == null) {
			icd = (IndexCategoryDefinition) IndexCategoryDefinitionFactory.getInstance().availableDefinitions().iterator().next();
		}
		getCategoryPart().setViewerInput(icd);

		// Register to the categorization viewer to update the picture list viewer
		getCategoryPart().getTreeViewer().addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				IStructuredSelection selection = (IStructuredSelection) event.getSelection();
				Object selectedCategory = selection.getFirstElement();
				getListPart().getTableViewer().setInput(selectedCategory);
			}
		});

		// Create a canvas
		previewPart.createWidgets(listForm);
		final Canvas canvas = previewPart.getCanvas();

		// Add a listener to the table view
		getListPart().getTableViewer().addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				IStructuredSelection selection = (IStructuredSelection) event.getSelection();
				if (selection.size() != 1) {
					previewPart.setSelectedImage(null);
				} else {
					previewPart.setSelectedImage((ImageContainer) selection.getFirstElement());
				}
			}
		});

		// Return the sash form control
		return mainForm;
		
	}

	/**
	 * Handle the situation when the application window
	 * gets closed. In this case do some cleanup work
	 * @author Chris
	 */
	public void cleanup()
	{

		getCategoryPart().getRootIndexCategory().writeIndex();	

		// Save the window information
		String maximized = getShell().getMaximized() ? "1" : "0";
		String minimized = getShell().getMinimized() ? "1" : "0";
		Point size = getShell().getSize();
		String width = "" + size.x;
		String height = "" + size.y;
		Point location = getShell().getLocation();
		String x = "" + location.x;
		String y = "" + location.y;
		Properties appProps = Application.getInstance().getApplicationProperties(); 
		appProps.setProperty(MainWindow.PROPNAME_MAXIMIZED, maximized);
		appProps.setProperty(MainWindow.PROPNAME_MINIMIZED, minimized);
		appProps.setProperty(MainWindow.PROPNAME_WIDTH, width);
		appProps.setProperty(MainWindow.PROPNAME_HEIGHT, height);
		appProps.setProperty(MainWindow.PROPNAME_X, x);
		appProps.setProperty(MainWindow.PROPNAME_Y, y);
		 
		Application.getInstance().saveApplicationProperties();
		
	}
	
	/**
	 * Handle the shell close event and some work which is
	 * necessary before closing the shell
	 * @author Chris
	 */
	protected void handleShellCloseEvent()
	{
		cleanup();
		super.handleShellCloseEvent();
	}
	
	/**
	 * Configure the shell
	 * @param Shell shell
	 * @author Chris
	 */
	protected void configureShell(Shell shell)
	{
		
		super.configureShell(shell);
		
		shell.setImage(ImageUtil.getImage("icons/logo.gif"));

		// Configure depending on the last remembered information
		Properties appProps = Application.getInstance().getApplicationProperties();
		
		// Set the shell maximized if necessary 
		String maximized = appProps.getProperty(MainWindow.PROPNAME_MINIMIZED, "0");
		boolean bmaximized = maximized.equals("1");
		shell.setMaximized(bmaximized);
		
		// Set the shell minimized if necessary
		boolean bminimized = false;
		if (bmaximized == false) {
			String minimized = appProps.getProperty(MainWindow.PROPNAME_MINIMIZED, "0");
			bminimized = minimized.equals("1");
			shell.setMinimized(bminimized);
		}
		
		// Set the size and position only if the window is not minimized or maximized
		if (bminimized == false & bmaximized == false) {

			// Set the size
			String width = appProps.getProperty(MainWindow.PROPNAME_WIDTH, "");
			String height = appProps.getProperty(MainWindow.PROPNAME_HEIGHT,"");
			if (width != "" & height != "") {
				int iwidth = Integer.parseInt(width);
				int iheight = Integer.parseInt(height);
				shell.setSize(iwidth, iheight);
			}

			// Set the location
			String x = appProps.getProperty(MainWindow.PROPNAME_X, "");
			String y = appProps.getProperty(MainWindow.PROPNAME_Y, "");
			if (x != "" & y != "") {
				int ix = Integer.parseInt(x);
				int iy = Integer.parseInt(y);
				shell.setLocation(ix, iy);
			}
		
		}

	}
	
	/**
	 * Set the edit menu
	 * @param MenuManager The menu manager to set to
	 * @author Chris
	 */
	protected void setEditMenu(MenuManager editMenu)
	{
		this.editMenu = editMenu;
	}

	/**
	 * Get the edit menu
	 * @return The menu manager
	 * @author Chris
	 */
	protected MenuManager getEditMenu()
	{
		return editMenu;
	}

	/**
	 * Set the file menu
	 * @param MenuManager The menu manager to set to
	 * @author Chris
	 */
	protected void setFileMenu(MenuManager fileMenu)
	{
		this.fileMenu = fileMenu;
	}

	/**
	 * Get the file menu
	 * @return The menu manager
	 * @author Chris
	 */
	protected MenuManager getFileMenu()
	{
		return fileMenu;
	}

	/**
	 * Set the view menu
	 * @param MenuManager The menu manager to set to
	 * @author Chris
	 */
	protected void setViewMenu(MenuManager viewMenu)
	{
		this.viewMenu = viewMenu;
	}

	/**
	 * Get the view menu
	 * @return The menu manager
	 * @author Chris
	 */
	protected MenuManager getViewMenu()
	{
		return viewMenu;
	}

	/**
	 * Get the category part of the window
	 * @return The category part of the window
	 * @author Chris
	 */
	public WindowCategoryPart getCategoryPart()
	{
		return categoryPart;
	}

	/**
	 * Get the picture list part of the window
	 * @return The picture list part of the window
	 * @author Chris
	 */
	public WindowListPart getListPart()
	{
		return listPart;
	}

	/**
	 * Refresh the category part
	 * @author Chris
	 */
	public void refreshCategoryPart()
	{
		getCategoryPart().getTreeViewer().refresh();
	}

	/**
	 * Refresh the list part
	 * @author Chris
	 */
	public void refreshListPart()
	{
		getListPart().getTableViewer().refresh();
	}

	/**
	 * Get the preview part of the window
	 * @return The preview part of the window
	 * @author Chris
	 */
	public WindowPreviewPart getPreviewPart()
	{
		return previewPart;
	}

	/**
	 * Return the selection from the picture list
	 * @return The selected object of the picture list viewer
	 * @author Chris
	 */
	public IStructuredSelection getPictureListSelection()
	{
		return getListPart().getSelection();
	}
	
	/**
	 * Return the selection of the category viewer
	 * @return The selected category in the index viewer
	 * @author Chris
	 */
	public IStructuredSelection getCategorizationViewerSelection()
	{
		return getCategoryPart().getSelection();
	}
	
	protected ToolBarManager createToolBarManager(int style)
	{
		ToolBarManager toolMgr = super.createToolBarManager(style);
		Iterator iterator = registeredWindowParts.iterator();
		while (iterator.hasNext())
		{
			WindowPart aPart = (WindowPart) iterator.next();
			aPart.registerToolBarActions(toolMgr);
		}
		return toolMgr;
	}
	
}
